home *** CD-ROM | disk | FTP | other *** search
/ Total Network Tools 2002 / NextStepPublishing-TotalNetworkTools2002-Win95.iso / Archive / Web Server / TinyWeb Server.EXE / SRC.ZIP / srvmain.pas next >
Encoding:
Pascal/Delphi Source File  |  2000-12-05  |  58.1 KB  |  1,988 lines

  1. //////////////////////////////////////////////////////////////////////////
  2. //
  3. //  TinyWeb Copyright (C) 2000 RITLABS S.R.L.
  4. //
  5. //  This programs is free for commercial and non-commercial use as long as
  6. //  the following conditions are aheared to.
  7. //
  8. //  Copyright remains RITLABS S.R.L., and as such any Copyright notices
  9. //  in the code are not to be removed. If this package is used in a
  10. //  product, RITLABS S.R.L. should be given attribution as the owner
  11. //  of the parts of the library used. This can be in the form of a textual
  12. //  message at program startup or in documentation (online or textual)
  13. //  provided with the package.
  14. //
  15. //  Redistribution and use in source and binary forms, with or without
  16. //  modification, are permitted provided that the following conditions are
  17. //  met:
  18. //
  19. //  1. Redistributions of source code must retain the copyright
  20. //     notice, this list of conditions and the following disclaimer.
  21. //  2. Redistributions in binary form must reproduce the above copyright
  22. //     notice, this list of conditions and the following disclaimer in the
  23. //     documentation and/or other materials provided with the distribution.
  24. //  3. All advertising materials mentioning features or use of this software
  25. //     must display the following acknowledgement:
  26. //     "Based on TinyWeb Server by RITLABS S.R.L.."
  27. //
  28. //  THIS SOFTWARE IS PROVIDED BY RITLABS S.R.L. "AS IS" AND ANY EXPRESS
  29. //  OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  30. //  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  31. //  DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
  32. //  ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  33. //  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
  34. //  GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  35. //  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
  36. //  IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
  37. //  OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  38. //  ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  39. //
  40. //  The licence and distribution terms for any publically available
  41. //  version or derivative of this code cannot be changed. i.e. this code
  42. //  cannot simply be copied and put under another distribution licence
  43. //  (including the GNU Public Licence).
  44. //
  45. //////////////////////////////////////////////////////////////////////////
  46.  
  47.  
  48. unit SrvMain;
  49.  
  50. interface
  51.  
  52. procedure ComeOn;
  53.  
  54. implementation
  55.  
  56. uses
  57.   WinSock,
  58.   Windows,
  59.   xBase;
  60.  
  61. const
  62.  
  63.   ScriptsPath = 'cgi-bin';
  64.  
  65.   CHTTPServerThreadBufSize = $2000;
  66.   MaxStatusCodeIdx = 36;
  67.   StatusCodes : array[0..MaxStatusCodeIdx] of record Code: Integer; Msg: string end =
  68.   ((Code:100; Msg:'Continue'),
  69.    (Code:101; Msg:'Switching Protocols'),
  70.    (Code:200; Msg:'OK'),
  71.    (Code:201; Msg:'Created'),
  72.    (Code:202; Msg:'Accepted'),
  73.    (Code:203; Msg:'Non-Authoritative Information'),
  74.    (Code:204; Msg:'No Content'),
  75.    (Code:205; Msg:'Reset Content'),
  76.    (Code:206; Msg:'Partial Content'),
  77.    (Code:300; Msg:'Multiple Choices'),
  78.    (Code:301; Msg:'Moved Permanently'),
  79.    (Code:302; Msg:'Moved Temporarily'),
  80.    (Code:303; Msg:'See Other'),
  81.    (Code:304; Msg:'Not Modified'),
  82.    (Code:305; Msg:'Use Proxy'),
  83.    (Code:400; Msg:'Bad Request'),
  84.    (Code:401; Msg:'Unauthorized'),
  85.    (Code:402; Msg:'Payment Required'),
  86.    (Code:403; Msg:'Forbidden'),
  87.    (Code:404; Msg:'Not Found'),
  88.    (Code:405; Msg:'Method Not Allowed'),
  89.    (Code:406; Msg:'Not Acceptable'),
  90.    (Code:407; Msg:'Proxy Authentication Required'),
  91.    (Code:408; Msg:'Request Time-out'),
  92.    (Code:409; Msg:'Conflict'),
  93.    (Code:410; Msg:'Gone'),
  94.    (Code:411; Msg:'Length Required'),
  95.    (Code:412; Msg:'Precondition Failed'),
  96.    (Code:413; Msg:'Request Entity Too Large'),
  97.    (Code:414; Msg:'Request-URI Too Large'),
  98.    (Code:415; Msg:'Unsupported Media Type'),
  99.    (Code:500; Msg:'Internal Server Error'),
  100.    (Code:501; Msg:'Not Implemented'),
  101.    (Code:502; Msg:'Bad Gateway'),
  102.    (Code:503; Msg:'Service Unavailable'),
  103.    (Code:504; Msg:'Gateway Time-out'),
  104.    (Code:505; Msg:'HTTP Version not supported'));
  105.  
  106. type
  107.   TEntityHeader = class;
  108.   TCollector = class;
  109.  
  110.   TAbstractHttpResponseData = class
  111.   end;
  112.  
  113.   THttpResponseDataFileHandle = class(TAbstractHttpResponseData)
  114.     FHandle: THandle;
  115.     constructor Create(AHandle: DWORD);
  116.   end;
  117.  
  118.   THttpResponseDataEntity = class(TAbstractHttpResponseData)
  119.     FEntityHeader : TEntityHeader;
  120.     constructor Create(AEntityHeader : TEntityHeader);
  121.   end;
  122.  
  123.   THttpResponseErrorCode = class(TAbstractHttpResponseData)
  124.     FErrorCode: Integer;
  125.     constructor Create(AErrorCode: Integer);
  126.   end;
  127.  
  128.   PHTTPServerThreadBufer = ^THTTPServerThreadBufer;
  129.   THTTPServerThreadBufer = array[0..CHTTPServerThreadBufSize-1] of Char;
  130.  
  131.   TPipeReadStdThread = class(TThread)
  132.     Error: Boolean;
  133.     HPipe: DWORD;
  134.     Buffer: PHTTPServerThreadBufer;
  135.     EntityHeader: TEntityHeader;
  136.     Collector: TCollector;
  137.     procedure Execute; override;
  138.   end;
  139.  
  140.   TPipeWriteStdThread = class(TThread)
  141.     HPipe: DWORD;
  142.     s: string;
  143.     procedure Execute; override;
  144.   end;
  145.  
  146.   TPipeReadErrThread = class(TThread)
  147.     HPipe: DWORD;
  148.     s: string;
  149.     procedure Execute; override;
  150.   end;
  151.  
  152.   TContentType = class
  153.     ContentType,
  154.     Extension: string;
  155.   end;
  156.  
  157.   TContentTypeColl = class(TSortedColl)
  158.     function Compare(Key1, Key2: Pointer): Integer; override;
  159.     function KeyOf(Item: Pointer): Pointer; override;
  160.   end;
  161.  
  162.   THTTPData = class;
  163.  
  164.   THTTPServerThread = class(TThread)
  165.     RemoteHost,
  166.     RemoteAddr: string;
  167.     Buffer: THTTPServerThreadBufer;
  168.     Socket: TSocket;
  169.     constructor Create;
  170.     procedure PrepareResponse(d: THTTPData);
  171.     procedure Execute; override;
  172.     destructor Destroy; override;
  173.   end;
  174.  
  175.   TGeneralHeader = class
  176.     CacheControl,            // Section 14.9
  177.     Connection,              // Section 14.10
  178.     Date,                    // Section 14.19
  179.     Pragma,                  // Section 14.32
  180.     TransferEncoding,        // Section 14.40
  181.     Upgrade,                 // Section 14.41
  182.     Via : string;            // Section 14.44
  183.     function Filter(const z, s: string): Boolean;
  184.     function OutString: string;
  185.   end;
  186.  
  187.  
  188.   TResponseHeader = class
  189.     Age,                    // Section 14.6
  190.     Location,               // Section 14.30
  191.     ProxyAuthenticate,      // Section 14.33
  192.     Public_,                // Section 14.35
  193.     RetryAfter,             // Section 14.38
  194.     Server,                 // Section 14.39
  195.     Vary,                   // Section 14.43
  196.     Warning,                // Section 14.45
  197.     WWWAuthenticate         // Section 14.46
  198.       : string;
  199.     function OutString: string;
  200.   end;
  201.  
  202.   TRequestHeader = class
  203.     Accept,                  // Section 14.1
  204.     AcceptCharset,           // Section 14.2
  205.     AcceptEncoding,          // Section 14.3
  206.     AcceptLanguage,          // Section 14.4
  207.     Authorization,           // Section 14.8
  208.     From,                    // Section 14.22
  209.     Host,                    // Section 14.23
  210.     IfModifiedSince,         // Section 14.24
  211.     IfMatch,                 // Section 14.25
  212.     IfNoneMatch,             // Section 14.26
  213.     IfRange,                 // Section 14.27
  214.     IfUnmodifiedSince,       // Section 14.28
  215.     MaxForwards,             // Section 14.31
  216.     ProxyAuthorization,      // Section 14.34
  217.     Range,                   // Section 14.36
  218.     Referer,                 // Section 14.37
  219.     UserAgent,               // Section 14.42
  220.     Cookie: string;          // rfc-2109
  221.     function Filter(const z, s: string): Boolean;
  222.   end;
  223.  
  224.   TCollector = class
  225.   private
  226.     Parsed: Boolean;
  227.     Lines: TStringColl;
  228.     CollectStr: string;
  229.     CollectLen: Integer;
  230.     ContentLength: Integer;
  231.   public
  232.     EntityBody: string;
  233.     GotEntityBody,
  234.     CollectEntityBody: Boolean;
  235.     function Collect(var Buf: THTTPServerThreadBufer; j: Integer): Boolean;
  236.     constructor Create;
  237.     destructor Destroy; override;
  238.     function GetNextLine: string;
  239.     function LineAvail: Boolean;
  240.     procedure SetContentLength(i: Integer);
  241.   end;
  242.  
  243.  
  244.   TEntityHeader = class
  245.     Allow,                   // Section 14.7
  246.     ContentBase,             // Section 14.11
  247.     ContentEncoding,         // Section 14.12
  248.     ContentLanguage,         // Section 14.13
  249.     ContentLength,           // Section 14.14
  250.     ContentLocation,         // Section 14.15
  251.     ContentMD5,              // Section 14.16
  252.     ContentRange,            // Section 14.17
  253.     ContentType,             // Section 14.18
  254.     ETag,                    // Section 14.20
  255.     Expires,                 // Section 14.21
  256.     LastModified,            // Section 14.29
  257.     {This is two headers for file download by CGI}
  258.     AcceptRanges,            // Section 14.5
  259.     ContentDisposition,      // Section 15.10
  260.     EntityBody: string;
  261.     EntityLength: Integer;
  262.     SetCookie,
  263.     CGIStatus,
  264.     CGILocation: string;
  265.     function Filter(const z, s: string): Boolean;
  266.     procedure CopyEntityBody(Collector: TCollector);
  267.     function OutString: string;
  268.   end;
  269.  
  270.   THTTPData = class
  271.     RequestCollector: TCollector;
  272.     FileNfo: TFileINfo;
  273.  
  274.     FHandle: THandle;
  275.     StatusCode,
  276.     HTTPVersionHi,
  277.     HTTPVersionLo: Integer;
  278.  
  279.     TransferFile,
  280.     ReportError,
  281.     KeepAlive: Boolean;
  282.  
  283.     ErrorMsg,
  284.     Method, RequestURI, HTTPVersion, AuthUser, AuthPassword, AuthType,
  285.     URIPath, URIParams, URIQuery, URIQueryParam : string;
  286.  
  287.     ResponceObjective: TAbstractHttpResponseData;
  288.  
  289.     RequestGeneralHeader: TGeneralHeader;
  290.     RequestRequestHeader: TRequestHeader;
  291.     RequestEntityHeader: TEntityHeader;
  292.  
  293.     ResponseGeneralHeader: TGeneralHeader;
  294.     ResponseResponseHeader: TResponseHeader;
  295.     ResponseEntityHeader: TEntityHeader;
  296.  
  297.     constructor Create;
  298.     destructor Destroy; override;
  299.  
  300.   end;
  301.  
  302. var
  303.   ContentTypes: TContentTypeColl;
  304.   ParamStr1,
  305.   FAccessLog,
  306.   FAgentLog,
  307.   FErrorLog,
  308.   FRefererLog: string;
  309.   CSAccessLog,
  310.   CSAgentLog,
  311.   CSErrorLog,
  312.   CSRefererLog: TRTLCriticalSection;
  313.   HAccessLog,
  314.   HAgentLog,
  315.   HErrorLog,
  316.   HRefererLog: DWORD;
  317.  
  318.  
  319. function FileTimeToStr(AT: DWORD): string;
  320. const
  321.   wkday: array[0..6] of string = ('Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat');
  322. var
  323.   D: TSystemTime;
  324.   T: TFileTime;
  325. begin
  326.   uCvtSetFileTime(AT, T.dwLowDateTime, T.dwHighDateTime);
  327.   if FileTimeToSystemTime(T, D) then
  328.   Result :=
  329.   wkday[D.wDayOfWeek] + ', ' +
  330.   ItoSz(D.wDay, 2) + ' ' +
  331.   MonthE(D.wMonth) + ' ' +
  332.   ItoS(D.wYear) + ' ' +
  333.   ItoSz(D.wHour, 2) + ':' +
  334.   ItoSz(D.wMinute, 2) + ':' +
  335.   ItoSz(D.wSecond, 2) + ' GMT';
  336. end;
  337.  
  338. function StrToFileTime(AStr: string): DWORD;
  339. var
  340.   D: TSystemTime;
  341.   T: TFileTime;
  342.   s, z: string;
  343.   e: Integer;
  344. begin
  345.   Result := INVALID_FILE_TIME;
  346.   Clear(D, SizeOf(D));
  347.   s := AStr;
  348.   GetWrd(s, z, ' ');
  349.   GetWrdD(s, z); Val(z, D.wDay, e); if e > 0 then Exit;
  350.   GetWrdA(s, z); D.wMonth := Pos(#1+UpperCase(z)+#1, #1'JAN'#1'FEB'#1'MAR'#1'APR'#1'MAY'#1'JUN'#1'JUL'#1'AUG'#1'SEP'#1'OCT'#1'NOV'#1'DEC'#1);
  351.   if D.wMonth = 0 then Exit;
  352.   D.wMonth := (D.wMonth+3) div 4;
  353.   GetWrdD(s, z); Val(z, D.wYear, e); if e > 0 then Exit;
  354.   if D.wYear < 200 then
  355.   begin
  356.     if D.wYear < 50 then Inc(D.wYear, 2000) else Inc(D.wYear, 1900);
  357.   end;
  358.   GetWrdD(s, z); Val(z, D.wHour, e); if e > 0 then Exit;
  359.   GetWrdD(s, z); Val(z, D.wMinute, e); if e > 0 then Exit;
  360.   GetWrdD(s, z); Val(z, D.wSecond, e); if e > 0 then Exit;
  361.   if not SystemTimeToFileTime(D, T) then Exit;
  362.   Result := uCvtGetFileTime(T.dwLowDateTime, T.dwHighDateTime);
  363. end;
  364.  
  365. // 'Sunday, 17-May-98 18:44:23 GMT; length=4956'
  366.  
  367. constructor THTTPServerThread.Create;
  368. begin
  369.   inherited Create(True);
  370. end;
  371.  
  372. destructor THTTPServerThread.Destroy;
  373. begin
  374.   FreeObject(Socket);
  375.   inherited Destroy;
  376. end;
  377.  
  378. function TGeneralHeader.Filter(const z, s: string): Boolean;
  379. begin
  380.   Result := True;
  381.   if z = 'CACHE-CONTROL'       then CacheControl       := s else // Section 14.9
  382.   if z = 'CONNECTION'          then Connection         := s else // Section 14.10
  383.   if z = 'DATE'                then Date               := s else // Section 14.19
  384.   if z = 'PRAGMA'              then Pragma             := s else // Section 14.32
  385.   if z = 'TRANSFER-ENCODING'   then TransferEncoding   := s else // Section 14.40
  386.   if z = 'UPGRADE'             then Upgrade            := s else // Section 14.41
  387.   if z = 'VIA'                 then Via                := s else // Section 14.44
  388.     Result := False;
  389. end;
  390.  
  391. function TRequestHeader.Filter(const z, s: string): Boolean;
  392. begin
  393.   Result := True;
  394.   if z = 'ACCEPT'              then Accept             := s else // Section 14.1
  395.   if z = 'ACCEPT-CHARSET'      then AcceptCharset      := s else // Section 14.2
  396.   if z = 'ACCEPT-ENCODING'     then AcceptEncoding     := s else // Section 14.3
  397.   if z = 'ACCEPT-LANGUAGE'     then AcceptLanguage     := s else // Section 14.4
  398.   if z = 'AUTHORIZATION'       then Authorization      := s else // Section 14.8
  399.   if z = 'FROM'                then From               := s else // Section 14.22
  400.   if z = 'HOST'                then Host               := s else // Section 14.23
  401.   if z = 'IF-MODIFIED-SINCE'   then IfModifiedSince    := s else // Section 14.24
  402.   if z = 'IF-MATCH'            then IfMatch            := s else // Section 14.25
  403.   if z = 'IF-NONE-MATCH'       then IfNoneMatch        := s else // Section 14.26
  404.   if z = 'IF-RANGE'            then IfRange            := s else // Section 14.27
  405.   if z = 'IF-UNMODIFIED-SINCE' then IfUnmodifiedSince  := s else // Section 14.28
  406.   if z = 'MAX-FORWARDS'        then MaxForwards        := s else // Section 14.31
  407.   if z = 'PROXY-AUTHORIZATION' then ProxyAuthorization := s else // Section 14.34
  408.   if z = 'RANGE'               then Range              := s else // Section 14.36
  409.   if z = 'REFERER'             then Referer            := s else // Section 14.37
  410.   if z = 'USER-AGENT'          then UserAgent          := s else // Section 14.42
  411.   if z = 'COOKIE'              then Cookie             := s else
  412.     Result := False
  413. end;
  414.  
  415. procedure Add(var s, z: string; const a: string);
  416. begin
  417.   if z <> '' then s := s + a + ': '+z+#13#10;
  418. end;
  419.  
  420. function TResponseHeader.OutString: string;
  421. var
  422.   s: string;
  423. begin
  424.   s := '';
  425.   Add(s, Age,               'Age');                // Section 14.6
  426.   Add(s, Location,          'Location');           // Section 14.30
  427.   Add(s, ProxyAuthenticate, 'Proxy-Authenticate'); // Section 14.33
  428.   Add(s, Public_,           'Public');             // Section 14.35
  429.   Add(s, RetryAfter,        'Retry-After');        // Section 14.38
  430.   Add(s, Server,            'Server');             // Section 14.39
  431.   Add(s, Vary,              'Vary');               // Section 14.43
  432.   Add(s, Warning,           'Warning');            // Section 14.45
  433.   Add(s, WWWAuthenticate,   'WWW-Authenticate');   // Section 14.46
  434.   Result := s;
  435. end;
  436.  
  437. function TEntityHeader.OutString: string;
  438. var
  439.   s: string;
  440. begin
  441.   s := '';
  442.   Add(s, Allow,           'Allow');             // Section 14.7
  443.   Add(s, ContentBase,     'Content-Base');      // Section 14.11
  444.   Add(s, ContentEncoding, 'Content-Encoding');  // Section 14.12
  445.   Add(s, ContentLanguage, 'Content-Language');  // Section 14.13
  446.   Add(s, ContentLength,   'Content-Length');    // Section 14.14
  447.   Add(s, ContentLocation, 'Content-Location');  // Section 14.15
  448.   Add(s, ContentMD5,      'Content-MD5');       // Section 14.16
  449.   Add(s, ContentRange,    'Content-Range');     // Section 14.17
  450.   Add(s, ContentType,     'Content-Type');      // Section 14.18
  451.   Add(s, ETag,            'ETag');              // Section 14.20
  452.   Add(s, Expires,         'Expires');           // Section 14.21
  453.   Add(s, LastModified,    'Last-Modified');     // Section 14.29
  454.   {This is two headers for file download by CGI}
  455.   Add(s, AcceptRanges,    'Accept-Ranges');    // Section 14.5
  456.   Add(s, ContentDisposition, 'Content-Disposition'); // Section 15.10
  457.   Add(s, SetCookie,       'Set-Cookie');
  458.   Result := s;
  459. end;
  460.  
  461. function TGeneralHeader.OutString: string;
  462. var
  463.   s: string;
  464. begin
  465.   s := '';
  466.   Add(s, CacheControl,     'Cache-Control');     // Section 14.9
  467.   Add(s, Connection,       'Connection');        // Section 14.10
  468.   Add(s, Date,             'Date');              // Section 14.19
  469.   Add(s, Pragma,           'Pragma');            // Section 14.32
  470.   Add(s, TransferEncoding, 'Transfer-Encoding'); // Section 14.40
  471.   Add(s, Upgrade,          'Upgrade');           // Section 14.41
  472.   Add(s, Via,              'Via');               // Section 14.44
  473.   Result := s;
  474. end;
  475.  
  476. procedure TEntityHeader.CopyEntityBody(Collector: TCollector);
  477. begin
  478.   EntityLength := Collector.ContentLength;
  479.   ContentLength := ItoS(Collector.ContentLength);
  480.   EntityBody := Copy(Collector.EntityBody, 1, EntityLength);
  481. end;
  482.  
  483. function TEntityHeader.Filter(const z, s: string): Boolean;
  484. begin
  485.   Result := True;
  486.   if z = 'ALLOW'            then Allow           := s else // 14.7
  487.   if z = 'CONTENT-BASE'     then ContentBase     := s else // 14.11
  488.   if z = 'CONTENT-ENCODING' then ContentEncoding := s else // 14.12
  489.   if z = 'CONTENT-LANGUAGE' then ContentLanguage := s else // 14.13
  490.   if z = 'CONTENT-LENGTH'   then ContentLength   := s else // 14.14
  491.   if z = 'CONTENT-LOCATION' then ContentLocation := s else // 14.15
  492.   if z = 'CONTENT-MD5'      then ContentMD5      := s else // 14.16
  493.   if z = 'CONTENT-RANGE'    then ContentRange    := s else // 14.17
  494.   if z = 'CONTENT-TYPE'     then ContentType     := s else // 14.18
  495.   if z = 'ETAG'             then ETag            := s else // 14.20
  496.   if z = 'EXPIRES'          then Expires         := s else // 14.21
  497.   if z = 'LAST-MODIFIED'    then LastModified    := s else // 14.29
  498.   {This is two headers for file download by CGI}
  499.   if z = 'ACCEPT-RANGES'    then AcceptRanges    := s else // 14.5
  500.   if z = 'CONTENT-DISPOSITION' then ContentDisposition := s else // 15.10
  501.   if z = 'STATUS'           then
  502.   CGIStatus       := s
  503.   else
  504.   if z = 'LOCATION'         then CGILocation     := s else
  505.   if z = 'SET-COOKIE'       then SetCookie       := s else
  506.     Result := False;
  507. end;
  508.  
  509. constructor THTTPData.Create;
  510. begin
  511.   inherited Create;
  512.   RequestCollector := TCollector.Create;
  513.   RequestGeneralHeader := TGeneralHeader.Create;
  514.   RequestRequestHeader := TRequestHeader.Create;
  515.   RequestEntityHeader := TEntityHeader.Create;
  516. end;
  517.  
  518. destructor THTTPData.Destroy;
  519. begin
  520.   FreeObject(RequestCollector);
  521.   FreeObject(RequestGeneralHeader);
  522.   FreeObject(RequestRequestHeader);
  523.   FreeObject(RequestEntityHeader);
  524.   FreeObject(ResponseGeneralHeader);
  525.   FreeObject(ResponseResponseHeader);
  526.   FreeObject(ResponseEntityHeader);
  527.   ZeroHandle(FHandle);
  528.   inherited Destroy;
  529. end;
  530.  
  531. procedure TCollector.SetContentLength(i: Integer);
  532. begin
  533.   ContentLength := i;
  534.   GotEntityBody := ContentLength <= Length(EntityBody);
  535. end;
  536.  
  537. function TCollector.LineAvail: Boolean;
  538. begin
  539.   Result := Lines.Count > 0;
  540. end;
  541.  
  542. function TCollector.GetNextLine: string;
  543. begin
  544.   Result := Lines[0]; Lines.AtFree(0);
  545. end;
  546.  
  547. function TCollector.Collect(var Buf: THTTPServerThreadBufer; j: Integer): Boolean;
  548. var
  549.   i,l: Integer;
  550. begin
  551.   if not CollectEntityBody then
  552.   begin
  553.     l := Length(CollectStr);
  554.     for i := 0 to j-1 do
  555.     begin
  556.       if l <= CollectLen then
  557.       begin
  558.         Inc(l, j + 100);
  559.         SetLength(CollectStr, l);
  560.       end;
  561.       Inc(CollectLen);
  562.       CollectStr[CollectLen] := Buf[i];
  563.       if (CollectLen >= 2) and (CollectStr[CollectLen] = #10) and (CollectStr[CollectLen-1] = #13) then
  564.       begin
  565.         if CollectLen = 2 then
  566.         begin
  567.           CollectEntityBody := True;
  568.           Dec(j, i+1);
  569.           if j > 0 then Move(Buf[i+1], Buf[0], j);
  570.           Break;
  571.         end else
  572.         begin
  573.           Lines.Add(Copy(CollectStr, 1, CollectLen-2));
  574.           CollectLen := 0;
  575.         end;
  576.       end;
  577.     end;
  578.   end;
  579.  
  580.   if CollectEntityBody then
  581.   begin
  582.     if (CollectEntityBody) and (j>0) then
  583.     begin
  584.       i := Length(EntityBody);
  585.       SetLength(EntityBody, i+j);
  586.       Move(Buf, EntityBody[i+1], j);
  587.     end;
  588.     GotEntityBody := ContentLength <= Length(EntityBody);
  589.   end;
  590.   Result := True;
  591. end;
  592.  
  593. constructor TCollector.Create;
  594. begin
  595.   inherited Create;
  596.   Lines := TStringColl.Create;
  597. //  Lines.LongString;
  598. end;
  599.  
  600. destructor TCollector.Destroy;
  601. begin
  602.   FreeObject(Lines);
  603.   inherited Destroy;
  604. end;
  605.  
  606.  
  607. procedure TPipeWriteStdThread.Execute;
  608. var
  609.   j: DWORD;
  610.   slen: Integer;
  611. begin
  612.   slen := Length(s);
  613.   if slen > 0 then WriteFile(HPipe, s[1], slen, j, nil);
  614. end;
  615.  
  616. function DoCollect(Collector: TCollector; EntityHeader: TEntityHeader; j: Integer; Buffer: THTTPServerThreadBufer): Boolean;
  617. var
  618.   s,z: string;
  619. begin
  620.   Result := True;
  621.   if not Collector.Collect(Buffer, j) then Result := False else
  622.   if Collector.CollectEntityBody then
  623.   if not Collector.Parsed then
  624.   begin
  625.     Collector.Parsed := True;
  626.     while Collector.LineAvail do
  627.     begin
  628.       s := Collector.GetNextLine;
  629.       if Length(s)<4 then begin Result := False; Break end else
  630.       begin
  631.         GetWrdStrictUC(s, z);
  632.         Delete(z, Length(z), 1);
  633.         if not EntityHeader.Filter(z, s) then
  634.         begin
  635.           // New Feature !!!
  636.         end;
  637.       end;
  638.     end;
  639.     Collector.SetContentLength(StoI(EntityHeader.ContentLength));
  640.   end;
  641. end;
  642.  
  643. procedure TPipeReadErrThread.Execute;
  644. var
  645.   ss: ShortString;
  646.   j: DWORD;
  647. begin
  648.   repeat
  649.     if not ReadFile(HPipe, ss[1], 250, j, nil) then Break;
  650.     ss[0] := Char(j);
  651.     s := s + ss;
  652.   until Terminated;
  653. end;
  654.  
  655.  
  656. procedure TPipeReadStdThread.Execute;
  657. var
  658.   j: DWORD;
  659. begin
  660.   repeat
  661.     if not ReadFile(HPipe, Buffer^, CHTTPServerThreadBufSize, j, nil) then Break;
  662.     Error := not DoCollect(Collector, EntityHeader, j, Buffer^);
  663.     if Error then Break;
  664.     if (Collector.ContentLength > 0) and (Collector.GotEntityBody) then Break;
  665.   until Terminated;
  666. end;
  667.  
  668. function ExecuteScript(const AExecutable, APath, AScript, AQueryParam, AEnvStr, AStdInStr: string; Buffer: THTTPServerThreadBufer; SelfThr: TThread; var ErrorMsg: string): TEntityHeader;
  669. var
  670.   SI: TStartupInfo;
  671.   PI: TProcessInformation;
  672.   Security: TSecurityAttributes;
  673.   Actually: DWORD;
  674.   si_r, si_w, so_r, so_w, se_r, se_w: THandle;
  675.   b: Boolean;
  676.   Collector: TCollector;
  677.   EntityHeader: TEntityHeader;
  678.   PipeReadStdThread: TPipeReadStdThread;
  679.   PipeWriteStdThread: TPipeWriteStdThread;
  680.   PipeReadErrThread: TPipeReadErrThread;
  681.   s: string;
  682.  
  683. function ReportGUI: string;
  684. var
  685.   d, n, e: string;
  686. begin
  687.   FSPlit(AExecutable, d, n, e);
  688.   Result := n+e+' is a GUI application';
  689. end;
  690.  
  691. begin
  692.   Result := nil;
  693.  
  694.   with Security do
  695.   begin
  696.     nLength := SizeOf(TSecurityAttributes);
  697.     lpSecurityDescriptor := nil;
  698.     bInheritHandle := True;
  699.   end;
  700.  
  701.   CreatePipe(si_r, si_w, @Security, 0);
  702.   CreatePipe(so_r, so_w, @Security, 0);
  703.   CreatePipe(se_r, se_w, @Security, 0);
  704.  
  705.   FillChar(SI, SizeOf(SI), 0);
  706.   SI.CB := SizeOf(SI);
  707.   SI.dwFlags := STARTF_USESTDHANDLES or STARTF_USESHOWWINDOW;
  708.   SI.hStdInput := si_r;
  709.   SI.hStdOutput := so_w;
  710.   SI.hStdError := se_w;
  711.   SI.wShowWindow := SW_HIDE;
  712.   if AExecutable = AScript then s := AExecutable else s := AExecutable + ' ' + AScript;
  713.   if AQueryParam <> '' then s := s + ' ' + AQueryParam;
  714.   s := DelSpaces(s);
  715.   b := CreateProcess(
  716.     nil,                     // pointer to name of executable module
  717.     PChar(s),                // pointer to command line string
  718.     @Security,               // pointer to process security attributes
  719.     @Security,               // pointer to thread security attributes
  720.     True,                    // handle inheritance flag
  721.     CREATE_SUSPENDED,        // creation flags
  722.     PChar(AEnvStr),          // pointer to new environment block
  723.     PChar(APath),            // pointer to current directory name
  724.     SI,                      // pointer to STARTUPINFO
  725.     PI                       // pointer to PROCESS_INFORMATION
  726.   );
  727.  
  728.   if b then
  729.   begin
  730.     {--$IFDEF CHECK_GUI}
  731.     if WaitForInputIdle(PI.hProcess, 0) = WAIT_TIMEOUT then
  732.     begin
  733.       ErrorMsg := ReportGUI;
  734.       TerminateProcess(PI.hProcess, 0);
  735.       CloseHandle(PI.hThread);
  736.       CloseHandle(PI.hProcess);
  737.       b := False;
  738.     end;
  739.     {--$ENDIF}
  740.   end else
  741.   begin
  742.     ErrorMsg := SysErrorMsg(GetLastError);
  743.   end;
  744.  
  745.   if not b then
  746.   begin
  747.     CloseHandles([si_r, si_w, so_r, so_w, se_r, se_w]);
  748.     Exit;
  749.   end;
  750.  
  751.   if AStdInStr = '' then
  752.   begin
  753.     PipeWriteStdThread := nil;
  754.   end else
  755.   begin
  756.     PipeWriteStdThread := TPipeWriteStdThread.Create(True);
  757.     PipeWriteStdThread.s := AStdInStr;
  758.     PipeWriteStdThread.HPipe := si_w;
  759.     PipeWriteStdThread.Suspended := False;
  760.   end;
  761.  
  762.   PipeReadErrThread := TPipeReadErrThread.Create(True);
  763.   PipeReadErrThread.HPipe := se_r;
  764.   PipeReadErrThread.Suspended := False;
  765.  
  766.   Collector := TCollector.Create;
  767.   EntityHeader := TEntityHeader.Create;
  768.   PipeReadStdThread := TPipeReadStdThread.Create(True);
  769.   PipeReadStdThread.Priority := tpLower;
  770.   PipeReadStdThread.Collector := Collector;
  771.   PipeReadStdThread.EntityHeader := EntityHeader;
  772.   PipeReadStdThread.Buffer := @Buffer;
  773.   PipeReadStdThread.HPipe := so_r;
  774.   PipeReadStdThread.Suspended := False;
  775.  
  776.   SelfThr.Priority := tpLowest;
  777.  
  778.   ResumeThread(PI.hThread);
  779.   WaitForSingleObject(PI.hProcess, INFINITE);
  780.   CloseHandle(PI.hThread);
  781.   CloseHandle(PI.hProcess);
  782.  
  783. // Close StdIn
  784.   CloseHandle(si_r);
  785.   if PipeWriteStdThread = nil then
  786.   begin
  787.     CloseHandle(si_w);
  788.   end else
  789.   begin
  790.     WaitForSingleObject(PipeWriteStdThread.Handle, INFINITE);
  791.     PipeWriteStdThread.Terminate;
  792.     FreeObject(PipeWriteStdThread);
  793.     CloseHandle(si_w);
  794.   end;
  795.  
  796. // Close StdErr
  797.  
  798.   CloseHandle(se_w);
  799.   PipeReadErrThread.Terminate;
  800.   WaitForSingleObject(PipeReadErrThread.Handle, INFINITE);
  801.   ErrorMsg := PipeReadErrThread.s;
  802.   FreeObject(PipeReadErrThread);
  803.   CloseHandle(se_r);
  804.  
  805. // Close StdOut
  806.   PipeReadStdThread.Terminate;
  807.   CloseHandle(so_w);
  808.   WaitForSingleObject(PipeReadStdThread.Handle, INFINITE);
  809.   SelfThr.Priority := tpNormal;
  810.  
  811.   while not PipeReadStdThread.Error do
  812.   begin
  813.     if not ReadFile(so_r, Buffer, CHTTPServerThreadBufSize, Actually, nil) then Break;
  814.     PipeReadStdThread.Error := not DoCollect(Collector, EntityHeader, Actually, Buffer);
  815.     if (Collector.ContentLength > 0) and (Collector.GotEntityBody) then Break;
  816.   end;
  817.   CloseHandle(so_r);
  818.  
  819.   if PipeReadStdThread.Error or not Collector.GotEntityBody then FreeObject(Collector);
  820.   FreeObject(PipeReadStdThread);
  821.   if Collector = nil then FreeObject(EntityHeader) else
  822.   begin
  823.     if Collector.ContentLength = 0 then
  824.     begin
  825.       Collector.ContentLength := Length(Collector.EntityBody);
  826.       EntityHeader.ContentLength := ItoS(Collector.ContentLength);
  827.     end;
  828.     EntityHeader.CopyEntityBody(Collector);
  829.     FreeObject(Collector);
  830.     Result := EntityHeader;
  831.   end;
  832. end;
  833.  
  834. procedure AddAgentLog(const AAgent: string);
  835. var
  836.   s: string;
  837.   b: DWORD;
  838.   slen: Integer;
  839. begin
  840.   s := AAgent + #13#10;
  841.   EnterCriticalSection(CSAgentLog);
  842.   slen := Length(s);
  843.   WriteFile(HAgentLog, s[1], slen, b, nil);
  844.   LeaveCriticalSection(CSAgentLog);
  845. end;
  846.  
  847.  
  848. procedure AddRefererLog(const ARefererSrc, ARefererDst: string);
  849. var
  850.   s: string;
  851.   b: DWORD;
  852.   slen: Integer;
  853. begin
  854.   if ARefererSrc = '' then Exit;
  855.   s := ARefererSrc + ' -> ' + ARefererDst + #13#10;
  856.   EnterCriticalSection(CSRefererLog);
  857.   slen := Length(s);
  858.   WriteFile(HRefererLog, s[1], slen, b, nil);
  859.   LeaveCriticalSection(CSRefererLog);
  860. end;
  861.  
  862. function CurTime: string;
  863. var
  864.   lt: TSystemTime;
  865.   b: Integer;
  866.   s: string;
  867. begin
  868.   GetLocalTime(lt);
  869.   b := TimeZoneBias;
  870.   if b < 0 then begin b := -b; s := s+'+' end else s := s + '-';
  871.   b := b div 60;
  872.   Result := '['+
  873.         ItoSz(lt.wDay, 2) + '/' +
  874.         MonthE(lt.wMonth) + '/' +
  875.         ItoS(lt.wYear) + ':' +
  876.         ItoSz(lt.wHour,2) + ':' +
  877.         ItoSz(lt.wMinute,2) + ':' +
  878.         ItoSz(lt.wSecond, 2) + ' ' +
  879.         s +
  880.         ItoSz(b div 60, 2) +
  881.         ItoSz(b mod 60, 2) +
  882.         ']';
  883. end;
  884.  
  885. procedure AddAccessLog(const ARemoteHost, ARequestLine, AHTTPVersion, AUserName: string; AStatusCode, ALength: Integer);
  886. var
  887.   authuser,z,k: string;
  888.   b: DWORD;
  889.   slen: Integer;
  890. begin
  891.   if ALength = -1 then z := '-' else z := ItoS(ALength);
  892.   if AHTTPVersion = '' then k := '' else k := ' ' + AHTTPVersion;
  893.   if AUserName = '' then authuser := '-' else authuser := AUserName;
  894.   z := ARemoteHost +  // Remote hostname (or IP number if DNS hostname is not available)
  895.        ' - ' +        // rfc-931
  896.        authuser+' '+  // The username as which the user has authenticated himself
  897.        CurTime+' '+   // Date and time of the request
  898.        '"' + ARequestLine + k + '" ' +  // The request line exactly as it came from the client
  899.        ItoS(AStatusCode) + ' ' + // The HTTP status code returned to the client
  900.        z+             // The content-length of the document transferred
  901.        #13#10;
  902.   EnterCriticalSection(CSAccessLog);
  903.   slen := Length(z);
  904.   WriteFile(HAccessLog, z[1], slen, b, nil);
  905.   LeaveCriticalSection(CSAccessLog);
  906. end;
  907.  
  908. procedure AddErrorLog(const AErr: string);
  909. var
  910.   s: string;
  911.   b: DWORD;
  912.   slen: Integer;
  913. begin
  914.   s := CurTime + ' '+ AErr + #13#10;
  915.   EnterCriticalSection(CSErrorLog);
  916.   slen := Length(s);
  917.   WriteFile(HErrorLog, s[1], slen, b, nil);
  918.   LeaveCriticalSection(CSErrorLog);
  919. end;
  920.  
  921. constructor THttpResponseDataEntity.Create(AEntityHeader : TEntityHeader);
  922. begin
  923.   inherited Create;
  924.   FEntityHeader := AEntityHeader;
  925. end;
  926.  
  927. constructor THttpResponseErrorCode.Create(AErrorCode: Integer);
  928. begin
  929.   inherited Create;
  930.   FErrorCode := AErrorCode;
  931. end;
  932.  
  933. constructor THttpResponseDataFileHandle.Create(AHandle: THandle);
  934. begin
  935.   FHandle := AHandle
  936. end;
  937.  
  938.  
  939. function OpenRequestedFile(const AFName: string; thr: THttpServerThread; d: THttpData): TAbstractHttpResponseData;
  940. var
  941.   I: Integer;
  942.   FHandle: THandle;
  943.   z: string;
  944. begin
  945. // Try to open Requested file
  946.   z := LowerCase(AFName);
  947.   if Copy(z, 1, Length(ParamStr1)) <> LowerCase(ParamStr1) then
  948.   begin
  949.     Result := THttpResponseErrorCode.Create(403);
  950.     Exit;
  951.   end;
  952.   if Copy(z, 1, Length(ParamStr1)+1+Length(ScriptsPath)+1) = ParamStr1+'\'+(ScriptsPath)+'\' then
  953.   begin
  954.     Result := THttpResponseErrorCode.Create(403);
  955.     Exit;
  956.   end;
  957.   FHandle := _CreateFile(AFName, [cRead, cSequentialScan]);
  958.   if FHandle = INVALID_HANDLE_VALUE then
  959.   begin
  960.     AddErrorLog('access to '+AFName+' failed for '+thr.RemoteHost+', reason: '+SysErrorMsg(GetLastError));
  961.     Result := THttpResponseErrorCode.Create(404);
  962.     Exit;
  963.   end;
  964.   if not GetFileNfoByHandle(FHandle, d.FileNfo) then
  965.   begin
  966.     Result := THttpResponseErrorCode.Create(404);
  967.     Exit;
  968.   end;
  969.   z := LowerCase(CopyLeft(ExtractFileExt(AFName),2));
  970.   if z <> '' then
  971.   begin
  972.     if not ContentTypes.Search(@z, I) then z := '' else z := TContentType(ContentTypes.FList^[I]).ContentType;
  973.   end;
  974.   if z = '' then z := 'text/plain';
  975.   d.ResponseEntityHeader := TEntityHeader.Create;
  976.   d.ResponseEntityHeader.ContentType := z;
  977.   d.ResponseEntityHeader.EntityLength := d.FileNfo.Size;
  978.   d.ResponseEntityHeader.LastModified := FileTimeToStr(d.FileNfo.Time);
  979.   d.ResponseGeneralHeader.Date := FileTimeToStr(uGetSystemTime);
  980.   Result := THttpResponseDataFileHandle.Create(FHandle);
  981. end;
  982.  
  983. function GetEnvStr(thr: THttpServerThread; d: THttpData; const PathInfo: string): string;
  984. var
  985.   s: string;
  986.   AuxS: string;
  987.   p: PByteArray;
  988.   j: Integer;
  989.  
  990.   procedure Add(const Name, Value: string); begin s := s + Name+'='+Value+#0 end;
  991.  
  992. begin
  993.   s := '';
  994.   p := Pointer(GetEnvironmentStrings);
  995.   j := 0; while (p^[j]<>0) or (p^[j+1]<>0) do Inc(j);
  996.   Inc(j);
  997.   SetLength(s, j);
  998.   Move(p^, s[1], j);
  999.   FreeEnvironmentStrings(Pointer(p));
  1000.   AuxS := PathInfo;
  1001.   Replace('\', '/', AuxS);
  1002.   if AuxS <> '' then AuxS := '/' + AuxS;
  1003.   Add('PATH_INFO', AuxS);
  1004.   if AuxS <> '' then AuxS := ParamStr1+'\'+PathInfo;
  1005.   Add('PATH_TRANSLATED', AuxS);
  1006.   Add('REMOTE_HOST', thr.RemoteHost);
  1007.   Add('REMOTE_ADDR', thr.RemoteAddr);
  1008.   Add('GATEWAY_INTERFACE', 'CGI/1.1');
  1009.   Add('SCRIPT_NAME', d.URIPath);
  1010.   Add('REQUEST_METHOD', d.Method);
  1011.   Add('HTTP_ACCEPT', d.RequestRequestHeader.Accept);                     // Section 14.1
  1012.   Add('HTTP_ACCEPT_CHARSET', d.RequestRequestHeader.AcceptCharset);      // Section 14.2
  1013.   Add('HTTP_ACCEPT_ENCODING', d.RequestRequestHeader.AcceptEncoding);    // Section 14.3
  1014.   Add('HTTP_ACCEPT_LANGUAGE', d.RequestRequestHeader.AcceptLanguage);    // Section 14.4
  1015.   Add('HTTP_FROM', d.RequestRequestHeader.From);                         // Section 14.22
  1016.   Add('HTTP_HOST', d.RequestRequestHeader.Host);                         // Section 14.23
  1017.   Add('HTTP_REFERER', d.RequestRequestHeader.Referer);                   // Section 14.37
  1018.   Add('HTTP_USER_AGENT', d.RequestRequestHeader.UserAgent);              // Section 14.42
  1019.   Add('HTTP_COOKIE', d.RequestRequestHeader.Cookie);
  1020.   Add('QUERY_STRING', d.URIQuery);
  1021.   Add('SERVER_SOFTWARE', CServerName);
  1022.   Add('SERVER_NAME', 'RITLABS S.R.L.');
  1023.   Add('SERVER_PROTOCOL', d.HTTPVersion);
  1024.   Add('SERVER_PORT', ItoS(thr.Socket.FPort));
  1025.   Add('CONTENT_TYPE', d.RequestEntityHeader.ContentType);
  1026.   Add('CONTENT_LENGTH', d.RequestEntityHeader.ContentLength);
  1027.   Add('USER_NAME', d.AuthUser);
  1028.   Add('USER_PASSWORD', d.AuthPassword);
  1029.   Add('AUTH_TYPE', d.AuthType);
  1030.   Result := s + #0;
  1031. end;
  1032.  
  1033. function ReturnNewLocation(const ALocation: string; d: THTTPData): TAbstractHttpResponseData;
  1034. begin
  1035.   d.ResponseResponseHeader.Location := ALocation;
  1036.   Result := THttpResponseErrorCode.Create(302);
  1037. end;
  1038.  
  1039. function IsURL(const s: string): Boolean;
  1040. begin
  1041.   Result := Pos('://', s) > 0;
  1042. end;
  1043.  
  1044. type
  1045.   TExecutableCache = class
  1046.     LocalFName, sResult: string;
  1047.     ReturnValue: HInst;
  1048.   end;
  1049.  
  1050.   TExecutableCacheColl = class(TSortedColl)
  1051.     function Compare(Key1, Key2: Pointer): Integer; override;
  1052.     function KeyOf(Item: Pointer): Pointer; override;
  1053.   end;
  1054.  
  1055. var
  1056.   ExecutableCache: TExecutableCacheColl;
  1057.  
  1058. function TExecutableCacheColl.Compare(Key1, Key2: Pointer): Integer;
  1059. begin
  1060.   Compare := CompareStr(PString(Key1)^, PString(Key2)^);
  1061. end;
  1062.  
  1063. function TExecutableCacheColl.KeyOf(Item: Pointer): Pointer;
  1064. begin
  1065.   Result := @TExecutableCache(Item).LocalFName;
  1066. end;
  1067.  
  1068. function FindExecutableCached(const LocalFName, sPath: string; var s: string): HInst;
  1069. var
  1070.   i: Integer;
  1071.   c: TExecutableCache;
  1072. begin
  1073.   ExecutableCache.Enter;
  1074.   if ExecutableCache.Search(@LocalFName, i) then
  1075.   begin
  1076.     c := ExecutableCache[i];
  1077.     s := StrAsg(c.sResult);
  1078.     Result := c.ReturnValue;
  1079.   end else
  1080.   begin
  1081.     SetLength(s, 1000);
  1082.     Result := FindExecutable(PChar(LocalFName), PChar(sPath), @s[1]);
  1083.     c := TExecutableCache.Create;
  1084.     c.ReturnValue := Result;
  1085.     c.LocalFName := StrAsg(LocalFName);
  1086.     if Result > 32 then
  1087.     begin
  1088.       SetLength(s, NulSearch(s[1]));
  1089.       c.sResult := StrAsg(s);
  1090.     end;
  1091.     ExecutableCache.AtInsert(i, c);
  1092.   end;
  1093.   ExecutableCache.Leave;
  1094. end;
  1095.  
  1096. type
  1097.   TRootCache = class
  1098.     FURI, FResult: string;
  1099.     IsCGI: Boolean;
  1100.   end;
  1101.  
  1102.   TRootCacheColl = class(TSortedColl)
  1103.     function Compare(Key1, Key2: Pointer): Integer; override;
  1104.     function KeyOf(Item: Pointer): Pointer; override;
  1105.   end;
  1106.  
  1107. var
  1108.   RootCacheColl: TRootCacheColl;
  1109.  
  1110.  
  1111. function TRootCacheColl.Compare(Key1, Key2: Pointer): Integer;
  1112. begin
  1113.   Compare := CompareStr(PString(Key1)^, PString(Key2)^);
  1114. end;
  1115.  
  1116. function TRootCacheColl.KeyOf(Item: Pointer): Pointer;
  1117. begin
  1118.   Result := @TRootCache(Item).FURI;
  1119. end;
  1120.  
  1121.  
  1122. function FindRootFileEx(const AURI: string; var IsCGI: Boolean): string;
  1123. var
  1124.   s, z: string;
  1125. begin
  1126.   IsCGI := False;
  1127.   Result := ParamStr1 + AURI + 'index.html';
  1128.   if FileExists(Result) then Exit;
  1129.   Result := ParamStr1 + AURI + 'index.htm';
  1130.   if FileExists(Result) then Exit;
  1131.   Result := ParamStr1 + AURI + 'index.html';
  1132.   s := GetEnvVariable('PATHEXT');
  1133.   while s <> '' do
  1134.   begin
  1135.     GetWrd(s, z, ';');
  1136.     if Length(z) < 2 then Continue;
  1137.     if z[1] <> '.' then Continue;
  1138.     z := ParamStr1+'\'+ScriptsPath+AURI+'index'+z;
  1139.     if FileExists(z) then begin Result := z; IsCGI := True; Exit end;
  1140.   end;
  1141. end;
  1142.  
  1143. function FindRootFile(const AURI: string; var IsCGI: Boolean): string;
  1144. var
  1145.   Found: Boolean;
  1146.   I: Integer;
  1147.   c: TRootCache;
  1148. begin
  1149.   RootCacheColl.Enter;
  1150.   Found := RootCacheColl.Search(@AURI, I);
  1151.   if Found then
  1152.   begin
  1153.     c := RootCacheColl[i];
  1154.     IsCGI := c.IsCGI;
  1155.     Result := StrAsg(c.FResult);
  1156.   end;
  1157.   RootCacheColl.Leave;
  1158.   if Found then Exit;
  1159.   Result := FindRootFileEx(AURI, IsCGI);
  1160.   RootCacheColl.Enter;
  1161.   if not RootCacheColl.Search(@AURI, I) then
  1162.   begin
  1163.     c := TRootCache.Create;
  1164.     c.FURI := StrAsg(AURI);
  1165.     c.FResult := StrAsg(Result);
  1166.     c.IsCGI := IsCGI;
  1167.     RootCacheColl.AtInsert(I, c);
  1168.   end;
  1169.   RootCacheColl.Leave;
  1170.  
  1171. end;
  1172.  
  1173.  
  1174.  
  1175.  
  1176. function WebServerHttpResponse(thr: THttpServerThread; d: THTTPData): TAbstractHttpResponseData;
  1177. var
  1178.   sPath, sName, sExt,
  1179.   s: string;
  1180.   LocalFName: string;
  1181.   ii: HInst;
  1182.   ResponseEntityHeader: TEntityHeader;
  1183.  
  1184.  
  1185. var
  1186.   CgiFile: string;
  1187.   PathInfo: string;
  1188.  
  1189.   // Thanks to Nick McDaniel, Intranaut Inc. (21 January 1999)
  1190.   // We were having problems with files that that had spaces in the name (C:\Program Files\).  The error that was being generated was "Internal Server Error: Can't open
  1191.   // To alievate this problem, we added double quotes to executable and script name
  1192.  
  1193. function QuoteSpaced(const s: string): string;
  1194. begin
  1195. // Thanks to Vladimir A. Bakhvaloff (30 January 2000)
  1196. // parameters to Pos() function were improperly ordered
  1197.   if Pos(' ', DelSpaces(s)) <= 0 then // Does the file name contain space cheracters inside?
  1198.   begin
  1199.     Result := s                 // No, return it as is
  1200.   end else
  1201.   begin
  1202.     Result := '"'+s+'"';        // Yes, add quotes
  1203.   end;
  1204. end;
  1205.  
  1206. procedure Exec;
  1207. begin
  1208.   ResponseEntityHeader := ExecuteScript(QuoteSpaced(s), sPath, QuoteSpaced(CgiFile), d.URIQueryParam, GetEnvStr(thr, d, PathInfo), d.RequestEntityHeader.EntityBody, thr.Buffer, thr, d.ErrorMsg);
  1209. end;
  1210.  
  1211.  
  1212. function CgiFileOK: Boolean;
  1213. var
  1214.   fa: DWord;
  1215.   z: string;
  1216. begin
  1217.   Result := False;
  1218.   fa := GetFileAttributes(PChar(ParamStr1+'\'+ScriptsPath));
  1219.   if fa = INVALID_HANDLE_VALUE then Exit;
  1220.   if (fa and FILE_ATTRIBUTE_DIRECTORY) = 0 then Exit;
  1221.   CgiFile := Copy(LocalFName, 1, Length(ParamStr1)+1+Length(ScriptsPath));
  1222.   PathInfo := CopyLeft(LocalFName, Length(CgiFile)+2);
  1223.   repeat
  1224.     GetWrd(PathInfo, z, '\');
  1225.     CgiFile := CgiFile + '\'+z;
  1226.     fa := GetFileAttributes(PChar(CgiFile));
  1227.     if fa = INVALID_HANDLE_VALUE then Exit;
  1228.     if (fa and FILE_ATTRIBUTE_DIRECTORY) = 0 then
  1229.     begin
  1230.       Result := True;
  1231.       Exit;
  1232.     end;
  1233.   until False;
  1234. end;
  1235.  
  1236. procedure RunCGI;
  1237. begin
  1238.     FSplit(CgiFile, sPath, sName, sExt);
  1239.     if UpperCase(sExt) = '.EXE' then
  1240.     begin
  1241.       s := CgiFile;
  1242.       Exec;
  1243.     end else
  1244.     begin
  1245.       ii := FindExecutableCached(CgiFile, sPath, s);
  1246.       if ii > 32 then
  1247.       begin
  1248.         if not FileExists(s) then
  1249.         begin
  1250.           d.ErrorMsg := SysErrorMsg(GetLastError) + ' ('+s+')';
  1251.         end else
  1252.         begin
  1253.           Exec;
  1254.         end;
  1255.       end else
  1256.       begin
  1257.         if ii = 31 then
  1258.         begin
  1259.           s := CgiFile;
  1260.           Exec;
  1261.         end else
  1262.         begin
  1263.           d.ErrorMsg := SysErrorMsg(ii);
  1264.         end;
  1265.       end;
  1266.     end;
  1267. end;
  1268.  
  1269. procedure MakeHeaders;
  1270. begin
  1271.   if ResponseEntityHeader = nil then
  1272.   begin
  1273.     if d.ErrorMsg = '' then
  1274.     begin
  1275.       d.ErrorMsg := 'CGI script '+d.URIPath+' returned nothing';
  1276.     end else
  1277.     begin
  1278.       d.ErrorMsg := 'Internal Server Error: '+d.ErrorMsg;
  1279.     end;
  1280.     Result := THttpResponseErrorCode.Create(500);
  1281.   end else
  1282.   begin
  1283.     if ResponseEntityHeader.CGILocation <> '' then
  1284.     begin
  1285.       if IsURL(ResponseEntityHeader.CGILocation) then
  1286.       begin
  1287.         Result := ReturnNewLocation(ResponseEntityHeader.CGILocation, d);
  1288.       end else
  1289.       begin
  1290.         Result := OpenRequestedFile(ResponseEntityHeader.CGILocation, thr, d);
  1291.       end;
  1292.     end else
  1293.     begin
  1294.       Result := THttpResponseDataEntity.Create(ResponseEntityHeader);
  1295.     end;
  1296.   end;
  1297. end;
  1298.  
  1299.  
  1300. var
  1301.   IsCGI: Boolean;
  1302.   CheckedURI: string;
  1303. begin
  1304.   ResponseEntityHeader := nil;
  1305.   s := d.URIPath;
  1306.  
  1307.   if Pos('\', s) > 0 then
  1308.   begin
  1309.     Result := THttpResponseErrorCode.Create(403);
  1310.     Exit;
  1311.   end;
  1312.  
  1313.   Replace('/', '\', s);
  1314.   if (s='') or (s[1]<>'\') then
  1315.   begin
  1316.     Result := THttpResponseErrorCode.Create(403);
  1317.     Exit;
  1318.   end;
  1319.   if (Pos('..', s)>0) or
  1320.      (Pos(':',s)>0) or
  1321.      (Pos('\\',s)>0) then
  1322.   begin
  1323.     Result := THttpResponseErrorCode.Create(403);
  1324.     Exit;
  1325.   end;
  1326.  
  1327.   CheckedURI := s;
  1328.   LocalFName := ParamStr1 + CheckedURI;
  1329.  
  1330.  
  1331. // Analyze file extension
  1332.   if LowerCase(Copy(d.URIPath, 2, Length(ScriptsPath)+1)) = (ScriptsPath + '/') then
  1333.   begin
  1334.     if CgiFileOK then RunCGI else d.ErrorMsg := SysErrorMsg(GetLastError);
  1335.     MakeHeaders;
  1336.     Exit;
  1337.   end;
  1338.  
  1339.   if CheckedURI[Length(CheckedURI)]='\' then
  1340.   begin
  1341.     LocalFName := FindRootFile(CheckedURI, IsCGI);
  1342.     if IsCGI then
  1343.     begin
  1344.       CgiFile := LocalFName;
  1345.       RunCGI;
  1346.       MakeHeaders;
  1347.       Exit;
  1348.     end;
  1349.   end else
  1350.   if ExtractFileExt(CheckedURI) = '' then
  1351.   begin
  1352.     Result := ReturnNewLocation(d.URIpath+'/', d);
  1353.     Exit;
  1354.   end;
  1355.  
  1356.  
  1357.   Result := OpenRequestedFile(LocalFName, thr, d);
  1358.  
  1359. end;
  1360.  
  1361. function HttpResponse(thr: THttpServerThread; d: THTTPData): TAbstractHttpResponseData;
  1362. begin
  1363.   Result := WebServerHttpResponse(thr, d);
  1364.   Exit;
  1365. end;
  1366.  
  1367. procedure THTTPServerThread.PrepareResponse(d: THTTPData);
  1368. var
  1369.   r: TAbstractHttpResponseData;
  1370.   rf: THttpResponseDataFileHandle absolute r;
  1371.   re: THttpResponseDataEntity absolute r;
  1372.   rc: THttpResponseErrorCode absolute r;
  1373. begin
  1374.   r := HttpResponse(Self, d);
  1375.   if r = nil then GlobalFail;
  1376.   if r is THttpResponseDataFileHandle then
  1377.   begin
  1378.     d.FHandle := rf.FHandle;
  1379.     d.TransferFile := True;
  1380.     d.ReportError := False;
  1381.     d.StatusCode := 200;
  1382.   end else
  1383.   if r is THttpResponseDataEntity then
  1384.   begin
  1385.     d.ResponseEntityHeader := re.FEntityHeader;
  1386.     d.ReportError := False;
  1387.     d.StatusCode := 200;
  1388.   end else
  1389.   if r is THttpResponseErrorCode then
  1390.   begin
  1391.     d.StatusCode := rc.FErrorCode;
  1392.   end else GlobalFail;
  1393.   FreeObject(r);
  1394. end;
  1395.  
  1396. procedure THTTPServerThread.Execute;
  1397. var
  1398.   FPOS: DWORD;
  1399.   i, j: Integer;
  1400.   s,z,k: string;
  1401.   d: THTTPData;
  1402.   AbortConnection: Boolean;
  1403.   Actually: DWORD;
  1404.  
  1405. begin
  1406.  
  1407.   if not Socket.Handshake then Exit;
  1408.  
  1409.   RemoteAddr := AddrInet(Socket.FAddr);
  1410.   RemoteHost := GetHostNameByAddr(Socket.FAddr);
  1411.  
  1412.   repeat
  1413.     AbortConnection := False;
  1414.     d := THTTPData.Create;
  1415.     d.StatusCode := 400;
  1416.     d.ReportError := True;
  1417.     d.ResponseGeneralHeader := TGeneralHeader.Create;
  1418.     if d.ResponseResponseHeader = nil then d.ResponseResponseHeader := TResponseHeader.Create;
  1419.     s := '';
  1420.     with d do repeat
  1421.  
  1422.       j := Socket.Read(Buffer, CHTTPServerThreadBufSize);
  1423.       if (j <= 0) or (Socket.Status <> 0) then Break;
  1424.  
  1425.       if not RequestCollector.Collect(Buffer, j) then Break;
  1426.       if not RequestCollector.CollectEntityBody then Continue;
  1427.  
  1428.       if not RequestCollector.Parsed then
  1429.       begin
  1430.         if not RequestCollector.LineAvail then Break;
  1431.         RequestCollector.Parsed := True;
  1432.  
  1433.     // Parse the request
  1434.         s := RequestCollector.GetNextLine;
  1435.  
  1436.         if not ProcessQuotes(s) then Break;
  1437.  
  1438.         GetWrdStrictUC(s, Method);    if s = '' then Break;
  1439.         GetWrdStrict(s, RequestURI);  if s = '' then Break;
  1440.         GetWrdStrict(s, HTTPVersion); if s <> '' then Break;
  1441.  
  1442.     // Parse HTTP version
  1443.         s := HTTPVersion;
  1444.         GetWrd(s, z, '/'); if z <> 'HTTP' then Break;
  1445.         GetWrd(s, z, '.');
  1446.         if not DigitsOnly(s) or not DigitsOnly(z) then Break;
  1447.         if not _Val(z, HttpVersionHi) then Break;
  1448.         if not _Val(s, HttpVersionLo) then Break;
  1449.  
  1450.         s := '';
  1451.         z := '';
  1452.  
  1453.         while RequestCollector.LineAvail do
  1454.         begin
  1455.           s := RequestCollector.GetNextLine;
  1456.           if Length(s)<4 then Break;
  1457.           GetWrdStrictUC(s, z);
  1458.           Delete(z, Length(z), 1);
  1459.           if not RequestGeneralHeader.Filter(z, s) and
  1460.              not RequestRequestHeader.Filter(z, s) and
  1461.              not RequestEntityHeader.Filter(z, s) then
  1462.           begin
  1463.             // New Feature !!!
  1464.           end;
  1465.  
  1466.           s := '';
  1467.           z := '';
  1468.         end;
  1469.  
  1470.         if (s <> '') or (z <> '') then Break;
  1471.         RequestCollector.SetContentLength(StoI(RequestEntityHeader.ContentLength));
  1472.       end;
  1473.  
  1474.       if not RequestCollector.GotEntityBody then Continue;
  1475.  
  1476.       // process intity body
  1477.       RequestEntityHeader.CopyEntityBody(RequestCollector);
  1478.  
  1479.       FreeObject(RequestCollector);
  1480.  
  1481.       KeepAlive := UpperCase(RequestGeneralHeader.Connection) = 'KEEP-ALIVE';
  1482.  
  1483.       if (Method <> 'GET') and
  1484.          (Method <> 'POST') and
  1485.          (Method <> 'HEAD') then
  1486.       begin
  1487.         StatusCode := 403;
  1488.         Break;
  1489.       end else
  1490.       begin
  1491.  
  1492.     // Parse URI
  1493.         s := RequestURI;
  1494.         i := Pos('?', s);
  1495.         if i > 0 then
  1496.         begin
  1497.           URIQuery := CopyLeft(s, i+1);
  1498.           DeleteLeft(s, i);
  1499.           if Pos('=', URIQuery) = 0 then
  1500.           begin
  1501.             URIQueryParam := URIQuery;
  1502.             if not UnpackPchars(URIQueryParam) then Break;
  1503.           end;
  1504.         end;
  1505.         i := Pos(';', s);
  1506.         if i > 0 then
  1507.         begin
  1508.           URIParams := CopyLeft(s, i+1);
  1509.           DeleteLeft(s, i);
  1510.         end;
  1511.         if not UnpackPchars(s) then Break;
  1512.         URIPath := s;
  1513.  
  1514.         AddRefererLog(d.RequestRequestHeader.Referer, d.URIPath);
  1515.         AddAgentLog(d.RequestRequestHeader.UserAgent);
  1516.  
  1517.         PrepareResponse(d);
  1518.  
  1519.         Break;
  1520.       end;
  1521.     until False;
  1522.  
  1523.   // Send a response
  1524.     with d do
  1525.     begin
  1526.       if ResponseEntityHeader = nil then ResponseEntityHeader := TEntityHeader.Create;
  1527.  
  1528.       if TransferFile and (RequestRequestHeader.IfModifiedSince <> '') then
  1529.       begin
  1530.         Actually := StrToFileTime(RequestRequestHeader.IfModifiedSince);
  1531.         if (Actually <> INVALID_FILE_TIME) and (StrToFileTime(ResponseEntityHeader.LastModified) = Actually) then
  1532.         begin
  1533.           ZeroHandle(FHandle);
  1534.           TransferFile := False;
  1535.           StatusCode := 304;
  1536.           ReportError := True;
  1537.         end;
  1538.       end;
  1539.  
  1540.       s := ResponseEntityHeader.CGIStatus;
  1541.       if s <> '' then
  1542.       begin
  1543.     k := s;
  1544.     GetWrd(k, z, ' ');
  1545.         Val(z, StatusCode, i);
  1546.     // Status code 200 was treated as error. Thanks to David Gommeren for pointing that out.
  1547.     if StatusCode <> 200 then ReportError := True;
  1548.       end else
  1549.       begin
  1550.  // Get Status Line
  1551.         for i := 0 to MaxStatusCodeIdx do if StatusCode = StatusCodes[i].Code then
  1552.         begin
  1553.           s := StatusCodes[i].Msg;
  1554.           Break;
  1555.         end;
  1556.         if s = '' then GlobalFail;
  1557.         if ErrorMsg = '' then ErrorMsg := s;
  1558.         s := ItoS(StatusCode)+ ' '+ s;
  1559.       end;
  1560.       if ReportError then
  1561.       begin
  1562.         KeepAlive := False;
  1563.         if ResponseEntityHeader.ContentType = '' then ResponseEntityHeader.ContentType := 'text/html';
  1564.         if ResponseEntityHeader.EntityBody = '' then ResponseEntityHeader.EntityBody :=
  1565.           '<HTML>'+
  1566.           '<TITLE>'+s+'</TITLE>'+
  1567.           '<BODY><H1>'+ErrorMsg+'</H1></BODY>'+
  1568.           '</HTML>';
  1569.         ResponseEntityHeader.EntityLength := Length(ResponseEntityHeader.EntityBody);
  1570.       end;
  1571.  
  1572.       ResponseEntityHeader.ContentLength := ItoS(ResponseEntityHeader.EntityLength);
  1573.  
  1574.       if KeepAlive then ResponseGeneralHeader.Connection := 'Keep-Alive';
  1575.  
  1576.       ResponseResponseHeader.Server := CServerName;
  1577.  
  1578.       if ReportError then i := -1 else i := ResponseEntityHeader.EntityLength;
  1579.       AddAccessLog(RemoteHost, Method + ' ' + URIPath, HTTPVersion, d.AuthUser, StatusCode,  i);
  1580.  
  1581.       s := 'HTTP/1.0 '+ s + #13#10+
  1582.         ResponseGeneralHeader.OutString+
  1583.         ResponseResponseHeader.OutString+
  1584.         ResponseEntityHeader.OutString+
  1585.         #13#10;
  1586.  
  1587.       if TransferFile then
  1588.       begin
  1589.         Socket.WriteStr(s);
  1590.         FPOS := 0;
  1591.         repeat
  1592.           ReadFile(FHandle, Buffer, CHTTPServerThreadBufSize, Actually, nil);
  1593.           Inc(FPOS, Actually);
  1594.           if FPOS > FileNfo.Size then Break;
  1595.           if Actually = 0 then Break;
  1596.           Actually := Socket.Write(Buffer, Actually);
  1597.         until (FPOS = FileNfo.Size) or (Actually < CHTTPServerThreadBufSize) or (Socket.Status <> 0);
  1598.         if FPOS <> FileNfo.Size then AbortConnection := True;
  1599.         ZeroHandle(FHandle);
  1600.       end else
  1601.       begin
  1602.         s := s + ResponseEntityHeader.EntityBody;
  1603.         Socket.WriteStr(s);
  1604.       end;
  1605.       AbortConnection := AbortConnection or not KeepAlive;
  1606.     end;
  1607.     FreeObject(d);
  1608.   until AbortConnection
  1609. end;
  1610.  
  1611.  
  1612. function TContentTypeColl.Compare(Key1, Key2: Pointer): Integer;
  1613. begin
  1614.   Compare := CompareStr(PString(Key1)^, PString(Key2)^);
  1615. end;
  1616.  
  1617. function TContentTypeColl.KeyOf(Item: Pointer): Pointer;
  1618. begin
  1619.   Result := @TContentType(Item).Extension;
  1620. end;
  1621.  
  1622. procedure GetContentTypes(const CBase, SubName: string; Swap: Boolean);
  1623. const
  1624.   ClassBufSize = 1000;
  1625. var
  1626.   Buf: array[0..ClassBufSize] of Char;
  1627.   r: TContentType;
  1628.   s, z, t : string;
  1629.   ec,
  1630.   i: Integer;
  1631.   Key,
  1632.   SubKey,
  1633.   BufSize,                       // size of string buffer
  1634.   cSubKeys,                      // number of subkeys
  1635.   cchMaxSubkey,                  // longest subkey name length
  1636.   cchMaxClass,                   // longest class string length
  1637.   cValues,                       // number of value entries
  1638.   cchMaxValueName,               // longest value name length
  1639.   cbMaxValueData,                // longest value data length
  1640.   cbSecurityDescriptor: DWORD;   // security descriptor length
  1641.   ftLastWriteTime: TFileTime;    // last write time
  1642. begin
  1643.   Key := OpenRegKeyEx(CBase, KEY_QUERY_VALUE or KEY_ENUMERATE_SUB_KEYS);
  1644.   BufSize := ClassBufSize;
  1645.   ec := RegQueryInfoKey(
  1646.     Key,                        // handle of key to query
  1647.     @Buf,
  1648.     @BufSize,
  1649.     nil,
  1650.     @cSubKeys,
  1651.     @cchMaxSubkey,
  1652.     @cchMaxClass,
  1653.     @cValues,
  1654.     @cchMaxValueName,
  1655.     @cbMaxValueData,
  1656.     @cbSecurityDescriptor,
  1657.     @ftLastWriteTime);
  1658.   if ec <> ERROR_SUCCESS then
  1659.   begin
  1660.     RegCloseKey(Key);
  1661.     Exit
  1662.   end;
  1663.   for i := 0 to cSubKeys-1 do
  1664.   begin
  1665.     BufSize := ClassBufSize;
  1666.     ec := RegEnumKeyEx(
  1667.       Key,
  1668.       i,
  1669.       Buf,
  1670.       BufSize,
  1671.       nil,
  1672.       nil, // address of buffer for class string
  1673.       nil, // address for size of class buffer
  1674.       @ftLastWriteTime);
  1675.     if ec <> ERROR_SUCCESS then Continue;
  1676.     SetString(s, Buf, BufSize);
  1677.     SubKey := OpenRegKey(CBase+'\'+s);
  1678.     if SubKey = INVALID_REGISTRY_KEY then Continue;
  1679.     z := ReadRegString(SubKey, SubName);
  1680.     RegCloseKey(SubKey);
  1681.     if Swap then
  1682.     begin
  1683.       t := s;
  1684.       s := z;
  1685.       z := t;
  1686.     end;
  1687.     z := LowerCase(CopyLeft(z,2));
  1688.     if (z = '') or (s = '') then Continue;
  1689.     if ContentTypes.Search(@z, ec) then Continue;
  1690.     r := TContentType.Create;
  1691.     r.ContentType := s;
  1692.     r.Extension := z;
  1693.     ContentTypes.AtInsert(ec, r);
  1694.   end;
  1695.   RegCloseKey(Key);
  1696. end;
  1697.  
  1698. type
  1699.   TAdrB = packed record
  1700.     A, B, C, D: Byte;
  1701.   end;
  1702.  
  1703.  
  1704. function _Adr2Int(const s: string): DWORD;
  1705.  
  1706. var
  1707.   CPos: Integer;
  1708.   Error: Boolean;
  1709.  
  1710. function Get: Byte;
  1711. var
  1712.   C: Char;
  1713.   R: Integer;
  1714.   err: Boolean;
  1715. begin
  1716.   Result := 0;
  1717.   if Error then Exit;
  1718.   err := False;
  1719.   R := Ord(S[CPos])-48;
  1720.   Inc(CPos);
  1721.   C := S[CPos];
  1722.   if (C >= '0') and (C <= '9') then
  1723.   begin
  1724.     R := R * 10 + (Ord(C)-48); Inc(CPos);
  1725.     C := S[CPos];
  1726.     if (C >= '0') and (C <= '9') then begin R := R * 10 + (Ord(C)-48); Inc(CPos) end else err := C <> '.';
  1727.   end else err := C <> '.';
  1728.   if (R > 255) or (err) then
  1729.   begin
  1730.     Error := True;
  1731.     Exit;
  1732.   end;
  1733.   Inc(CPos);
  1734.   Result := R;
  1735. end;
  1736.  
  1737. var
  1738.   A: TAdrB;
  1739. begin
  1740.   Error := False;
  1741.   CPos := 1;
  1742.   A.A := Get;
  1743.   A.B := Get;
  1744.   A.C := Get;
  1745.   A.D := Get;
  1746.   if Error then Result := INADDR_NONE else Result := PInteger(@A)^;
  1747. end;
  1748.  
  1749. function Adr2Int(const s: string): Integer;
  1750. begin
  1751.   Result := _Adr2Int(s+'.');
  1752. end;
  1753.  
  1754.  
  1755. var
  1756.   BindPort, BindAddr: DWORD;
  1757.   IsCGI: Boolean;
  1758. function GetHomeDir: Boolean;
  1759. var
  1760.   s: string;
  1761.   i: DWORD;
  1762. begin
  1763.   Result := False;
  1764.   if ParamCount < 1 then
  1765.   begin
  1766.     MessageBox(0, 'Path to home directory is absent!'#13#10+
  1767.                   'See READ.ME for details.'#13#10#13#10+
  1768.                   CServerName+' service failed to start.',
  1769.                   CServerName, CMB_FAILED);
  1770.     Exit;
  1771.   end;
  1772.   ParamStr1 := ParamStr(1);
  1773.   if ParamStr1[Length(ParamStr1)] = '\' then Delete(ParamStr1, Length(ParamStr1), 1);
  1774.   s := FindRootFile('\', IsCGI);
  1775.   if not FileExists(s) then
  1776.   begin
  1777.     s := 'Access to "'+s+'" failed'#13#10'Reason: "'+SysErrorMsg(GetLastError)+'"'#13#10#13#10+
  1778.     CServerName+' service failed to start';
  1779.     MessageBox(0, PChar(s), CServerName, CMB_FAILED);
  1780.     Exit;
  1781.   end;
  1782.   BindPort := 80;
  1783.   BindAddr := _INADDR_ANY;
  1784.   if ParamCount > 1 then
  1785.   begin
  1786.     i := Vl(ParamStr(2));
  1787.     if i <> INVALID_VALUE then BindPort := i;
  1788.   end;
  1789.   if ParamCount > 2 then
  1790.   begin
  1791.     i := Adr2Int(ParamStr(3));
  1792.     if i <> INVALID_VALUE then BindAddr := i;
  1793.   end;
  1794.   Result := True;
  1795. end;
  1796.  
  1797. procedure ReadContentTypes;
  1798. begin
  1799.   ContentTypes := TContentTypeColl.Create;
  1800.   GetContentTypes('SOFTWARE\Classes\MIME\Database\Content Type', 'Extension', False);
  1801.   GetContentTypes('SOFTWARE\Classes', 'Content Type', True);
  1802. end;
  1803.  
  1804. procedure InitLogs;
  1805. begin
  1806.   FAccessLog := 'access_log';
  1807.   FAgentLog := 'agent_log';
  1808.   FErrorLog := 'error_log';
  1809.   FRefererLog := 'referer_log';
  1810.   if not _LogOK(FAccessLog, HAccessLog) or
  1811.      not _LogOK(FAgentLog, HAgentLog) or
  1812.      not _LogOK(FErrorLog, HErrorLog) or
  1813.      not _LogOK(FRefererLog, HRefererLog) then GlobalFail;
  1814.   InitializeCriticalSection(CSAccessLog);
  1815.   InitializeCriticalSection(CSAgentLog);
  1816.   InitializeCriticalSection(CSErrorLog);
  1817.   InitializeCriticalSection(CSRefererLog);
  1818. end;
  1819.  
  1820. procedure InitReseterThread;
  1821. begin
  1822.   SocketsColl := TColl.Create;
  1823.   ResetterThread := TResetterThread.Create;
  1824. end;
  1825.  
  1826. procedure FreeDummyLibraries;
  1827. var
  1828.   I: Integer;
  1829. begin
  1830.   I := GetModuleHandle('OleAut32'); if I <> 0 then FreeLibrary(I); 
  1831.   I := GetModuleHandle('Ole32'); if I <> 0 then FreeLibrary(I);
  1832.   I := GetModuleHandle('RPCRT4'); if I <> 0 then FreeLibrary(I);
  1833.   I := GetModuleHandle('AdvAPI32'); if I <> 0 then FreeLibrary(I);
  1834.   I := GetModuleHandle('GDI32'); if I <> 0 then FreeLibrary(I);
  1835.   I := GetModuleHandle('COMCTL32'); if I <> 0 then FreeLibrary(I);
  1836.   I := GetModuleHandle('USER32'); if I <> 0 then FreeLibrary(I);
  1837. end;
  1838.  
  1839. procedure MainLoop;
  1840. var
  1841.   J, err: Integer;
  1842.   NewSocketHandle,
  1843.   ServerSocketHandle: WinSock.TSocket;
  1844.   NewSocket: TSocket;
  1845.   NewThread: THTTPServerThread;
  1846.   WData: TWSAData;
  1847.   Addr: TSockAddr;
  1848.   s: string;
  1849. begin
  1850.   err := WSAStartup(MakeWord(1,1), WData);
  1851.   if err <> 0 then
  1852.   begin
  1853.     s := 'Failed to initialize WinSocket,error #'+ItoS(err);
  1854.     MessageBox(0, PChar(s), CServerName, CMB_FAILED);
  1855.     Halt;
  1856.   end;
  1857.   ServerSocketHandle := socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
  1858.   if ServerSocketHandle = INVALID_SOCKET then
  1859.   begin
  1860.     s := 'Failed to create a socket, Error #'+ItoS(WSAGetLastError);
  1861.     MessageBox(0, PChar(s), CServerName, CMB_FAILED);
  1862.     Halt;
  1863.   end;
  1864.  
  1865.   Addr.sin_family := AF_INET;
  1866.   Addr.sin_port := htons(BindPort);
  1867.   Addr.sin_addr.s_addr := BindAddr;
  1868.   if bind(ServerSocketHandle, Addr, SizeOf(Addr)) = SOCKET_ERROR then
  1869.   begin
  1870.     S := 'Failed to bind the socket, error #'+ItoS(WSAGetLastError)+'.'#13#10#13#10+
  1871.          'Probable reason is that another daemon is already running on the same port ('+ItoS(BindPort)+').';
  1872.     MessageBox(0, PChar(S), CServerName, CMB_FAILED);
  1873.     Halt;
  1874.   end;
  1875.  
  1876.  
  1877.   InitReseterThread;
  1878.  
  1879.   listen(ServerSocketHandle, 5);
  1880.  
  1881.   FreeDummyLibraries;
  1882.  
  1883.   repeat
  1884.     J := SizeOf(Addr);
  1885.     {$IFDEF VER90}
  1886.     NewSocketHandle := accept(ServerSocketHandle, Addr, J);
  1887.     {$ELSE}
  1888.     NewSocketHandle := accept(ServerSocketHandle, @Addr, @J);
  1889.     {$ENDIF}
  1890.     if NewSocketHandle = INVALID_SOCKET then Break;
  1891.  
  1892.     {$IFDEF BEHIND_TUNNEL}
  1893.     if recv(NewSocketHandle, Addr.sin_addr.s_addr, 4, 0) <> 4 then
  1894.     begin
  1895.       closesocket(NewSocketHandle);
  1896.       Continue;
  1897.     end;
  1898.     {$ENDIF}
  1899.  
  1900.  
  1901.     NewSocket := TSocket.Create;
  1902.     NewSocket.Handle := NewSocketHandle;
  1903.     NewSocket.FAddr := Addr.sin_addr.s_addr;
  1904.     NewSocket.FPort := Addr.sin_port;
  1905.     if not NewSocket.Startup then FreeObject(NewSocket) else
  1906.     begin
  1907.       SocketsColl.Enter;
  1908.       if SocksCount = 0 then
  1909.       begin
  1910.         ResetterThread.TimeToSleep := SleepQuant;
  1911.         SetEvent(ResetterThread.oSleep);
  1912.       end;
  1913.       Inc(SocksCount);
  1914.       SocketsColl.Leave;
  1915.       NewThread := THTTPServerThread.Create;
  1916.       NewThread.FreeOnTerminate := True;
  1917.       NewThread.Socket := NewSocket;
  1918.       NewSocket.RegisterSelf;
  1919.       NewThread.Resume;
  1920.     end;
  1921.   until False;
  1922.   CloseSocket(ServerSocketHandle);
  1923. end;
  1924.  
  1925.  
  1926.  
  1927. procedure ComeOn;
  1928. var
  1929.   i: Integer;
  1930. begin
  1931.  
  1932. //--- Set Hight priority class
  1933. //  SetPriorityClass(GetCurrentProcess, HIGH_PRIORITY_CLASS);
  1934.  
  1935. //--- Initialize xBase Module
  1936.   xBaseInit;
  1937.  
  1938.   ExecutableCache := TExecutableCacheColl.Create;
  1939.   ExecutableCache.Enter;
  1940.   ExecutableCache.Leave;
  1941.  
  1942.   RootCacheColl := TRootCacheColl.Create;
  1943.   RootCacheColl.Enter;
  1944.   RootCacheColl.Leave;
  1945.  
  1946. //--- Get and validate a home directory
  1947.   if not GetHomeDir then Exit;
  1948.  
  1949.  
  1950.  
  1951. //--- Read content types from registry and associate with file extensions
  1952.   ReadContentTypes;
  1953.  
  1954. // --- Open log files and initialize semaphores
  1955.   InitLogs;
  1956.  
  1957. // --- Perform main loop
  1958.   MainLoop;
  1959.  
  1960. // Non-debug version never exits :-)
  1961.  
  1962.   ResetterThread.Terminate;
  1963.   SetEvent(ResetterThread.oSleep);
  1964.   SocketsColl.Enter;
  1965.   for i := 0 to SocketsColl.Count-1 do shutdown(TSocket(SocketsColl[i]).Handle, 2);
  1966.   SocketsColl.Leave;
  1967.   while SocketsColl.Count > 0 do Sleep(1000);
  1968.   ResetterThread.TimeToSleep := SleepQuant;
  1969.   SetEvent(ResetterThread.oSleep);
  1970.   WaitForSingleObject(ResetterThread.Handle, INFINITE);
  1971.   FreeObject(ResetterThread);
  1972.   FreeObject(SocketsColl);
  1973.   FreeObject(ContentTypes);
  1974.   xBaseDone;
  1975.   CloseHandle(HAccessLog);
  1976.   CloseHandle(HAgentLog);
  1977.   CloseHandle(HErrorLog);
  1978.   CloseHandle(HRefererLog);
  1979.   DeleteCriticalSection(CSAccessLog);
  1980.   DeleteCriticalSection(CSAgentLog);
  1981.   DeleteCriticalSection(CSErrorLog);
  1982.   DeleteCriticalSection(CSRefererLog);
  1983. end;
  1984.  
  1985. end.
  1986.  
  1987.  
  1988.